package com.dynamicthreadpool.spi.compiler;

import com.dynamicthreadpool.spi.extension.SPI;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Collections;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * JDK编译器实现
 * 从Dubbo 2.7版本提取
 */
@SPI("jdk")
public class JdkCompiler implements Compiler {

    private static final Pattern PACKAGE_PATTERN = Pattern.compile("package\\s+([\\w\\.]+);");
    private static final Pattern CLASS_PATTERN = Pattern.compile("class\\s+(\\w+)(?:\\s+extends\\s+[\\w\\.]+)?(?:\\s+implements\\s+[\\w\\.,\\s]+)?\\s*");

    @Override
    public Class<?> compile(String code, ClassLoader classLoader) {
        Matcher pm = PACKAGE_PATTERN.matcher(code);
        String pkg = null;
        if (pm.find()) {
            pkg = pm.group(1);
        }

        Matcher cm = CLASS_PATTERN.matcher(code);
        String cls = null;
        if (cm.find()) {
            cls = cm.group(1);
        }

        if (pkg == null || cls == null) {
            throw new IllegalArgumentException("Error compiling " + code + " , missing package or class.");
        }

        String className = pkg + "." + cls;
        File outputDir = new File(System.getProperty("java.io.tmpdir"));
        File sourceFile = new File(outputDir, className.replace('.', '/') + ".java");
        sourceFile.getParentFile().mkdirs();
        try {
            try (OutputStream out = new FileOutputStream(sourceFile)) {
                out.write(code.getBytes(Charset.forName("UTF-8")));
            }

            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            if (compiler == null) {
                throw new IllegalStateException("JDK compiler unavailable");
            }

            StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
            try {
                Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(Collections.singletonList(sourceFile));
                compiler.getTask(null, fileManager, null, null, null, compilationUnits).call();
            } finally {
                fileManager.close();
            }

            URL[] urls = {outputDir.toURI().toURL()};
            try (URLClassLoader loader = URLClassLoader.newInstance(urls, classLoader)) {
                return loader.loadClass(className);
            }
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        } finally {
            // 清理临时文件
            if (sourceFile.exists()) {
                sourceFile.delete();
            }
            File classFile = new File(outputDir, className.replace('.', '/') + ".class");
            if (classFile.exists()) {
                classFile.delete();
            }
        }
    }
}
